import threading
import queue
import time
from enum import Enum

class State(Enum):
    RUNNING = 0
    WAITING = 1
    STOPPED = 3

class CircularQueue:
    def __init__(self, size=6):
        self.size = size
        self.queue = [None] * size
        self.head = 0  # 出队位置
        self.tail = 0  # 入队位置
        self.count = 0
        self.lock = threading.Lock()

    def enqueue(self, item):
        with self.lock:
            if self.is_full():
                raise ValueError("Queue is full")
            self.queue[self.tail] = item
            self.tail = (self.tail + 1) % self.size
            self.count += 1
            return True

    def dequeue(self):
        with self.lock:
            if self.is_empty():
                raise ValueError("Queue is empty")
            item = self.queue[self.head]
            self.queue[self.head] = None
            self.head = (self.head + 1) % self.size
            self.count -= 1
            return item

    def is_empty(self):
        return self.count == 0

    def is_full(self):
        return self.count == self.size

    def __len__(self):
        return self.count

class ProducerConsumer:
    def __init__(self, prod_ticks, cons_ticks, tick_duration, buffer_size=6):
        self.prod_ticks = prod_ticks
        self.cons_ticks = cons_ticks
        self.tick_duration = tick_duration
        self.buffer = CircularQueue(size=buffer_size)
        self.prod_thread = threading.Thread(target=self.__producer)
        self.cons_thread = threading.Thread(target=self.__consumer)
        self.running = False
        self.paused = False
        self.prod_remaining_ticks = prod_ticks
        self.cons_remaining_ticks = cons_ticks
        self.prod_state = State.STOPPED
        self.cons_state = State.STOPPED
        
        self.__internal_paused = False
        self.__lock = threading.Lock()
        self.__prod_tick_end_event = threading.Event()
        self.__cons_tick_end_event = threading.Event()
        self.__started_time = None
        self.__ticks = 0  # 添加内部计时器变量
        self.__timer_thread = None
        self.__timer_running = False

    @property  
    def ticks(self):
        return self.__ticks

    def __timer(self):
        while self.__timer_running:
            if not self.paused and not self.__internal_paused:
                time.sleep(self.tick_duration)
                self.__ticks += 1
            else:
                time.sleep(self.tick_duration)

    def start(self):
        self.running = True
        self.__internal_paused = False
        self.cons_thread.start()
        self.prod_thread.start()
        
        # 启动计时器线程
        self.__timer_running = True
        self.__timer_thread = threading.Thread(target=self.__timer)
        self.__timer_thread.start()

        self.prod_state = State.RUNNING
        self.cons_state = State.WAITING
        self.prod_remaining_ticks = self.prod_ticks
        self.cons_remaining_ticks = self.cons_ticks

    def pause(self):
        self.__internal_paused = True
        def __pause():
            self.__prod_tick_end_event.wait()
            self.__cons_tick_end_event.wait()
            # 由于要等待两个线程完成当前刻，同时不要开始执行下一刻，
            # 必须先设置 self.paused = True，再等待事件
            # 但是这样会导致实际暂停的时刻比 paused 变量设置的时刻晚。
            # 因此多设置了一个 __internal_paused 变量
            self.paused = True
        threading.Thread(target=__pause).start()
        
    def resume(self):
        self.__internal_paused = False
        self.paused = False
    
    def stop(self):
        self.running = False
        self.__timer_running = False  # 停止计时器
        self.prod_thread.join()
        self.cons_thread.join()
        if self.__timer_thread:
            self.__timer_thread.join()
        self.prod_state = State.STOPPED
        self.cons_state = State.STOPPED

    def next(self):
        with self.__lock:
            self.__internal_paused = False
            self.__producer_tick()
            self.__consumer_tick()
            self.__ticks += 1
            self.__internal_paused = True

    def prev(self):
        with self.__lock:
            self.__internal_paused = False
            if self.prod_remaining_ticks < self.prod_ticks:
                self.prod_remaining_ticks += 1
            elif not self.buffer.is_empty():
                self.buffer.dequeue()
                self.prod_remaining_ticks = 1

            if self.cons_remaining_ticks < self.cons_ticks:
                self.cons_remaining_ticks += 1
            elif not self.buffer.is_full():
                self.buffer.enqueue(1)
                self.cons_remaining_ticks = 1
            self.__internal_paused = True

    def __producer(self):
        while self.running:
            self.__prod_tick_end_event.clear()
            if not self.__internal_paused:
                time.sleep(self.tick_duration)
                self.__producer_tick()
            else:
                time.sleep(self.tick_duration)
            self.__prod_tick_end_event.set()

    def __consumer(self):
        while self.running:
            self.__cons_tick_end_event.clear()
            if not self.__internal_paused:
                time.sleep(self.tick_duration)
                self.__consumer_tick()
            else:
                time.sleep(self.tick_duration)
            self.__cons_tick_end_event.set()

    def __producer_tick(self):
        if self.prod_remaining_ticks == 0:
            self.prod_remaining_ticks = self.prod_ticks
        if not self.buffer.is_full():
            self.prod_remaining_ticks -= 1
            self.prod_state = State.RUNNING
            if self.prod_remaining_ticks == 0:
                self.buffer.enqueue(1)
                self.prod_remaining_ticks = self.prod_ticks
        else:
            self.prod_state = State.WAITING
        
    def __consumer_tick(self):
        if self.cons_remaining_ticks == 0:
            self.cons_remaining_ticks = self.cons_ticks
        if not self.buffer.is_empty():
            self.cons_remaining_ticks -= 1
            self.cons_state = State.RUNNING
            if self.cons_remaining_ticks == 0:
                self.buffer.dequeue()
        else:
            self.cons_state = State.WAITING

if __name__ == "__main__":

    # 示例使用
    pc = ProducerConsumer(prod_ticks=5, cons_ticks=3, tick_duration=1)
    pc.start()
    # time.sleep(10)
    while True:
        # print(pc.ticks)
        print(
            pc.ticks,
            'prod', pc.prod_state, pc.prod_remaining_ticks,
            'cons', pc.cons_state, pc.cons_remaining_ticks,
            pc.buffer.count
        )
        time.sleep(0.5)
    # pc.pause()
    # time.sleep(5)
    # pc.next()
    # time.sleep(5)
    # pc.prev()
    # time.sleep(5)
    # pc.stop()